我们的Variant类型的主要设计是为了管理动态值的存储，也就是当前存储在Variant中的值。不同的类型可能需要考虑不同的大小和对齐方式。此外，该变体还需要存储一个辨别器，来指示哪些可能的类型是动态值的类型。一种简单的(尽管低效)存储机制直接使用元组(参见第25章):

\hspace*{\fill} \\ %插入空行
\noindent
\textit{variant/variantstorageastuple.hpp}
\begin{lstlisting}[style=styleCXX]
template<typename... Types>
class Variant {
	public:
	Tuple<Types...> storage;
	unsigned char discriminator;
};
\end{lstlisting}

标识符充当元组的动态索引。只有静态索引等于当前discriminator值的元组元素才有效。所以当discriminator为0时，get<0>(storage)提供了对动态值的访问;当discriminator为1时，get<1>(storage)提供对动态值的访问，以此类推。

可以构建核心操作is<T>()，并在元组上使用get<T>()。这样做非常低效，因为即使一次只有一个动态值，Variant本身现在需要的存储等于所有可能值类型的大小之和。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}这种方法还有许多其他问题，比如要求Types中的所有类型都有默认构造函数。
\end{tcolorbox}

更好的方法是将每个可能类型的存储重叠，可以通过递归地解开Variant的头和尾来实现，但这里使用的是联合，不是类:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{variant/variantstorageasunion.hpp}
\begin{lstlisting}[style=styleCXX]
template<typename... Types>
union VariantStorage;

template<typename Head, typename... Tail>
union VariantStorage<Head, Tail...> {
	Head head;
	VariantStorage<Tail...> tail;
};

template<>
union VariantStorage<> {
};
\end{lstlisting}

联合保证有足够的大小和对齐方式，以允许在相应时间内存储Types中的类型。但联合本身就很难处理，因为实现Variant将使用继承，这是联合不允许的。

不过，我们改变了Variant存储的底层表示:足够大的字符数组，以容纳任何类型，并对任何类型具有适当的对齐方式，我们将其用作存储活动值的缓冲区。VariantStorage类模板实现了这个缓冲区和一个辨别器:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{variant/variantstorage.hpp}
\begin{lstlisting}[style=styleCXX]
#include <new> // for std::launder()

template<typename... Types>
class VariantStorage {
	using LargestT = LargestType<Typelist<Types...>>;
	alignas(Types...) unsigned char buffer[sizeof(LargestT)];
	unsigned char discriminator = 0;
	public:
	unsigned char getDiscriminator() const { return discriminator; }
	void setDiscriminator(unsigned char d) { discriminator = d; }
	
	void* getRawBuffer() { return buffer; }
	const void* getRawBuffer() const { return buffer; }
	
	template<typename T>
		T* getBufferAs() { return std::launder(reinterpret_cast<T*>(buffer)); }
	template<typename T>
		T const* getBufferAs() const {
			return std::launder(reinterpret_cast<T const*>(buffer));
		}
};
\end{lstlisting}

使用在第24.2.2节中开发的LargestType元程序来计算缓冲区的大小，确保对于任何类型都足够大。类似地，alignas包扩展确保缓冲区具有适合任何值类型的对齐方式。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}可以使用模板元程序来计算最大对齐数(而不是使用对齐的包扩展)。两种方法的结果都是相同的，可将上面的公式将对齐计算工作移到了编译时。
\end{tcolorbox}

缓冲区本质上是联合的物理底层表示。可以使用getBuffer()访问指向缓冲区的指针，并通过显式类型转换、new(创建新值)和显式销毁(销毁创建的值)来操作存储。若不熟悉getBufferAs()中使用的std::launder()，那也没关系，现在只要知道它不修改地返回参数就足够了;我们将在讨论Variant模板的赋值操作符时解释其作用(参见第26.4.3节)。






































